/**
 * Copyright 2008 Wink Saville
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License version 3
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/* Only one allowed */
if (typeof McMgr == "undefined")
{
    /**
     * Constructor for the McMgr
     */
    function _mcMgr()
    {
        debug("_mcMgr: Enter");

        this._msgQueue = new LinkList();
        this._idHash = new Object();
        this._nameHash = new Object();
        this._tickId = 0;
        this._wakeup = function ()
        {
            if (this._tickId)
            {
                debug("_mcMgr: wakeup this._tickId=" + this._tickId);
                clearTimeout(this._tickId);
                setTimeout("McMgr.dispatcher()", 0);
            }
        }
        
        this.amp = new AjaxMsgPipe();

        debug("_mcMgr: start running sessionId=" + this.amp.sessionId);
        this.dispatcher();
        debug("_mcMgr: first time complete");
    }

    /**
     * Registger an Mc
     */
    _mcMgr.prototype.register = function (mc, handler)
    {
        debug("register: mc.name=" + mc.name);
        /**
         * Check if it already exits and throw an error!
         */
        if (this.lookup(mc.name) < 0)
        {
            debug("register: ask server mc.name=" + mc.name);
            mc.mcId = this.amp.register(mc.name);
            if (mc.mcId.id > 0)
            {
                mc.mcId.remote = false;
                this._idHash[mc.mcId.id] = mc
                this._nameHash[mc.name] = mc
                debug("register: mc.id=" + mc.mcId.id);
            }
        }
        else
        {
            throw new Error("Mc " + name + " already exists");
        }
    }
    
    /**
     * Lookup an mc
     */
    _mcMgr.prototype.lookup = function (name)
    {
        var mc = this._nameHash[name];
        if (mc == null)
        {
            /* TODO: Cache and define a forwarding handler??? */
            return this.amp.lookup(name);
        }
        else
        {
            return mc.mcId.id;
        }
    }

    /**
     * Get a message
     */
    _mcMgr.prototype.getMsg = function ()
    {
        var msg = new Msg();

        //debug("getMsg: _id " + msg._id);
        return msg;
    }

    /**
     * Return a message
     */
    _mcMgr.prototype.retMsg = function (msg)
    {
        //debug("retMsg: _id " + msg._id);
    }

    /**
     * Duplicate a message
     */
    _mcMgr.prototype.dupMsg = function (msg)
    {
        var newMsg;

        newMsg = this.getMsg();

        newMsg.remote = msg.remote;
        newMsg.dstId = msg.dstId;
        newMsg.dstSubId = msg.dstSubId;
        newMsg.srcId = msg.srcId;
        newMsg.srcSubId = msg.srcSubId;
        newMsg.guid = msg.guid;
        newMsg.cmd = msg.cmd;
        newMsg.tag = msg.tag;
        newMsg.timeout = msg.timeout;
        newMsg.status = msg.status;
        newMsg.arg1 = msg.arg1;
        newMsg.arg2 = msg.arg2;
        newMsg.arg3 = msg.arg3;
        newMsg.arg4 = msg.arg4;

        newMsg.obj = msg.obj;
        
        debug("dupMsg: _id " + newMsg._id);
        
        return newMsg;
    }

    /**
     * Get a reply message
     */
    _mcMgr.prototype.getReplyMsg = function (msg, status)
    {
        newMsg = this.getMsg();

        newMsg.remote = msg.remote;
        newMsg.dstId = msg.srcId;
        newMsg.dstSubId = msg.srcSubId;
        newMsg.srcId = msg.dstId;
        newMsg.srcSubId = msg.dstSubId;
        newMsg.guid = msg.guid;
        newMsg.cmd = msg.cmd | McConst.CMD_REPLY;
        newMsg.tag = msg.tag;
        newMsg.timeout = msg.timeout;
        newMsg.status = msg.status;
        newMsg.arg1 = msg.arg1;
        newMsg.arg2 = msg.arg2;
        newMsg.arg3 = msg.arg3;
        newMsg.arg4 = msg.arg4;

        newMsg.obj = msg.obj;
        
        debug("getReplyMsg: _id " + newMsg._id);
        
        return newMsg;
    }

    /**
     * Send a message
     */
    _mcMgr.prototype.sendMsg = function (msg)
    {
        if (msg.next != null)
            throw new Error("msg is already on a link list");
        this._msgQueue.append(msg);
        this._wakeup();
        //debug("McMgr.send: _id " + msg._id);
    }

    /**
     * Send a reply message
     */
    _mcMgr.prototype.sendReplyMsg = function (msg, status)
    {
        if (msg.next != null)
            throw new Error("msg is already on a link list");
        replyMsg = this.getReplyMsg(msg, status);
        this.sendMsg(replyMsg);
    }

    /**
     * Dispatcher of messages to mc's
     */
    _mcMgr.prototype.dispatcher = function ()
    {
        //debug("dispatcher: running");

        this._tickId = 0;

        var workWasDone = false;
        do	
        {
            var msg = this._msgQueue.pop();
            if (msg != null)
            {
                //debug("dispatcher: msg._id " + msg._id);
                var mc = this._idHash[msg.dstId];
                if (mc != null)
                {
                    if ((mc.handler != null) && (typeof mc.handler == "function"))
                    {
                        //debug("dispatcher: call " + mc.name + ".handler");
                        mc.handler(msg);
                    }
                    else
                    {
                        debug("run: NO handler for " + mc.name);
                    }
                }
                else
                {
                    //debug("dispatcher: no such mc locally; forwarding " + msg.dstId);
                    this.amp.forwardMsg(msg);
                }
                McMgr.retMsg(msg);
            }
        }
        while (msg != null);
        this._tickId = setTimeout("McMgr.dispatcher()", 60000);
        //debug("dispatcher: done _tickId " + this._tickId);
    }


    /**
     * Instantiate the mc manager
     */
    McMgr = new _mcMgr();
}
